package my.convert;

import my.convert.annotation.DateFormat;
import my.convert.attribute.processor.MyPropertyEditorRegistrySupport;
import my.convert.entity.impl.MyEntityAnalyzer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.beans.PropertyEditor;
import java.beans.PropertyEditorSupport;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * 单例的，首先获取实例，再调用getBean()
 *
 * @author xuanhu
 */
public class Map2Bean {
    public Logger logger = LoggerFactory.getLogger(getClass());
    public static MyEntityAnalyzer mea;// 类属性分析器
    private static MyPropertyEditorRegistrySupport p;
    private static Map2Bean instance = new Map2Bean();
    private static Map<Class, Map<String, String>> classPropertiesOfxiahua2tuofeng = new HashMap<Class, Map<String, String>>();
    private static Map<Class, Map<String, String>> classPropertiesOftuofeng2xiahua = new HashMap<Class, Map<String, String>>();

    private Map2Bean() {
        mea = new MyEntityAnalyzer();
        p = new MyPropertyEditorRegistrySupport();
    }

    public static Map2Bean getInstance() {
        return instance;
    }

    /**
     * @return void
     * @Author xh
     * @Description 扩展属性的类型转换支持
     * @Date 11:55 2019/6/25
     * @Param [clazz, pe]
     **/
    public void addEditor(Class<?> clazz, PropertyEditorSupport pe) {
        p.addEditor(clazz, pe);
    }

    public void removeEditor(Class<?> clazz) {
        p.removeEditor(clazz);
    }

    /**
     * @param <T>
     * @param entityClass
     * @param map
     * @param entity      可以为空。当entity不为空，且为entityClass类型的对象时，该方法，会把map中的值，赋值到entity对象中。
     * @return
     * @throws Exception
     */
    public <T> T getBean(Class<T> entityClass, Map<String, Object> map, Object entity) throws Exception {
        if (entity == null)
            entity = entityClass.newInstance();
        Set<Entry<String, Object>> entrySet = map.entrySet();
        for (Entry<String, Object> entry : entrySet) {
            if (entry.getValue() == null) continue;
            this.setAttributeValue(entityClass, entity, entry.getKey(), entry.getValue());
        }
        return (T) entity;
    }

    public <T> T getBean(Class<T> entityClass, Map<String, Object> map) throws Exception {
        return getBean(entityClass, map, (Object) null);
    }

    /**
     * @throws IllegalAccessException
     * @throws InstantiationException
     * @throws
     * @Title: getBean
     * @Description: TODO 将javaBean的属性的值赋给entityClass的对象中的同名属性
     * @param: @param entityClass
     * @param: @param javaBean
     * @param: @return
     * @return: T
     * @date 2018年4月20日 上午7:53:12   @author xuanhu
     */
    public <T> T getBeanFromBean(Class<T> entityClass, Object javaBean) throws Exception {
        return getBeanFromBean(entityClass,null, javaBean);
    }

    public <T> T getBeanFromBean(Class<T> entityClass, T targetObject , Object javaBean) throws Exception {
        T result = targetObject;
        if (javaBean == null) throw new RuntimeException("参数javaBean不能为空");

        if (result == null) result = entityClass.newInstance();
        Class<? extends Object> class1 = javaBean.getClass();
        Field[] fields = this.getAllFields(class1);
        Field[] fields_ = this.getAllFields(entityClass);
        if (fields_ == null || fields_.length == 0) return result;
        for (int i = 0, len = fields.length; i < len; i++) {

            Field field_ = null;
            for (Field f : fields_) {
                if (f.getName().equals(fields[i].getName())) {
                    field_ = f;
                    break;
                }
            }
            if (field_ == null) continue;//找不到属性 进下一次循环

            setPropertyValueFromFieldTofield_(javaBean, result, field_, fields[i]);
        }

        return result;
    }

    /**
     * @return T
     * @Author xh
     * @Description 驼峰属性 与 下划线属性 的对象转换。xiahuaClass=true，表示entityClass的属性是下划线风格的，否则为驼峰风格的。xiahuaObject同理。
     * @Date 15:32 2019/3/18
     * @Param [entityClass, javaBean, tuofengClass, tuofengObject]
     **/
    public <T> T getBean(Class<T> entityClass, Object javaBean, boolean xiahuaClass, boolean xiahuaObject) throws Exception {
        T result = null;
        if (javaBean == null) throw new RuntimeException("参数javaBean不能为空");

        result = entityClass.newInstance();
        Class<? extends Object> class1 = javaBean.getClass();
        Field[] fields = this.getAllFields(class1);
        Field[] fields_ = this.getAllFields(entityClass);
        if (fields_ == null || fields_.length == 0) return result;

        Map<String, String> xiahua2tuofeng = classPropertiesOfxiahua2tuofeng.get(class1);
        Map<String, String> tuofeng2xiahua = classPropertiesOftuofeng2xiahua.get(class1);

        for (int i = 0, len = fields.length; i < len; i++) {
            Field field_ = null;
            if (xiahuaClass != xiahuaObject) {
                if (xiahuaClass) {

                    if (tuofeng2xiahua == null) {
                        tuofeng2xiahua = new HashMap<>();
                        classPropertiesOftuofeng2xiahua.put(class1, tuofeng2xiahua);
                    }
                    String field_name = tuofeng2xiahua.get(fields[i].getName());
                    if (field_name == null) {
                        field_name = underCase(fields[i].getName());
                        tuofeng2xiahua.put(fields[i].getName(), field_name);
                    }
                    for (Field f : fields_) {
                        if (f.getName().equals(field_name)) {
                            field_ = f;
                            break;
                        }
                    }
                    if (field_ == null) continue;//找不到属性 进下一次循环

                }

                if (xiahuaObject) {
                    if (xiahua2tuofeng == null) {
                        xiahua2tuofeng = new HashMap<>();
                        classPropertiesOfxiahua2tuofeng.put(class1, xiahua2tuofeng);
                    }
                    String field_name = xiahua2tuofeng.get(fields[i].getName());
                    if (field_name == null) {
                        field_name = camelCase(fields[i].getName());
                        xiahua2tuofeng.put(fields[i].getName(), field_name);
                    }

                    for (Field f : fields_) {
                        if (f.getName().equals(field_name)) {
                            field_ = f;
                            break;
                        }
                    }
                    if (field_ == null) continue;//找不到属性 进下一次循环
                }
            } else {

                for (Field f : fields_) {
                    if (f.getName().equals(fields[i].getName())) field_ = f;
                }
                if (field_ == null) continue;//找不到属性 进下一次循环
            }


            setPropertyValueFromFieldTofield_(javaBean, result, field_, fields[i]);
        }

        return result;
    }

    /**
     * 转换为下划线
     *
     * @param camelCaseName
     * @return
     */
    public static String underCase(String camelCaseName) {
        StringBuilder result = new StringBuilder();
        if (camelCaseName != null && camelCaseName.length() > 0) {
            result.append(camelCaseName.substring(0, 1).toLowerCase());
            for (int i = 1; i < camelCaseName.length(); i++) {
                char ch = camelCaseName.charAt(i);
                if (Character.isUpperCase(ch)) {
                    result.append("_");
                    result.append(Character.toLowerCase(ch));
                } else {
                    result.append(ch);
                }
            }
        }
        return result.toString();
    }

    /**
     * 转换为驼峰
     *
     * @param underscoreName
     * @return
     */
    private String camelCase(String underscoreName) {
        StringBuilder result = new StringBuilder();
        if (underscoreName != null && underscoreName.length() > 0) {
            boolean flag = false;
            for (int i = 0; i < underscoreName.length(); i++) {
                char ch = underscoreName.charAt(i);
                if ("_".charAt(0) == ch) {
                    flag = true;
                } else {
                    if (flag) {
                        result.append(Character.toUpperCase(ch));
                        flag = false;
                    } else {
                        result.append(ch);
                    }
                }
            }
        }
        return result.toString();
    }

    private <T> void setPropertyValueFromFieldTofield_(Object javaBean, T result, Field field_, Field field) throws IllegalAccessException {
        Class<?> type1 = field.getType();
        field.setAccessible(true);
        field_.setAccessible(true);
        Class<?> type2 = field_.getType();
        Object value = field.get(javaBean);
        if (value == null) return;
        //如果是集合类型、或数组类型，或引用类型，不转换，直接赋引用.否则，是基本类型，转成entityClass同名属性对应的类型
        if (!Collection.class.isAssignableFrom(type1) && !type1.isArray()) {
            value = basicGetObjec(p, type2, value);
        }
        field_.set(result, value);
    }

    private Field[] getAllFields(Class clazz) {
        List<Field> fieldList = new ArrayList<>();
        while (clazz != null) {
            fieldList.addAll(new ArrayList<>(Arrays.asList(clazz.getDeclaredFields())));
            clazz = clazz.getSuperclass();
        }
        List<Field> fs = fieldList.stream().filter(f -> !f.getName().equals("serialVersionUID")).collect(Collectors.toList());
        Field[] fields = new Field[fs.size()];
        fs.toArray(fields);
        return fields;
    }

    private Field getField(Class clazz, String fieldName) {
        Field declaredField = null;
        while (declaredField == null && clazz != null) {
            try {
                declaredField = clazz.getDeclaredField(fieldName);
            } catch (NoSuchFieldException e) {
            }
            clazz = clazz.getSuperclass();
        }
        return declaredField;
    }

    private Method getMethod(Class clazz, String methodName) {
        Method declaredMethod = null;
        while (declaredMethod == null && clazz != null) {
            try {
                declaredMethod = clazz.getDeclaredMethod(methodName);
            } catch (NoSuchMethodException e) {
            }
            clazz = clazz.getSuperclass();
        }
        return declaredMethod;
    }

    /**
     * @param entity
     * @return
     * @throws Exception
     * @Title: getMap
     * @Description: 把bean中有getter方法的属性放入 map集合中,如果参数map为空，则创建一个Map返回
     * @return: Map<String, Object>
     * @date 2018年7月10日 上午10:35:51   @author xuanhu
     */
    public Map<String, Object> getMap(Object entity, Map<String, Object> map) throws Exception {
        if (entity == null) throw new RuntimeException("entity is null");
        Class<? extends Object> class1 = entity.getClass();
        Field[] fields = this.getAllFields(class1);
        if (map == null) map = new HashMap<String, Object>(fields.length);
        StringBuilder sb = new StringBuilder();
        String fieldname = "";
        Object invoke = null;
        sb.append("get");
        if (fields != null && fields.length > 0) for (Field field : fields) {
            sb.delete(3, sb.length());
            fieldname = field.getName();
            sb.append(fieldname.substring(0, 1).toUpperCase()).append(fieldname.substring(1));
            Method getter = getMethod(class1, sb.toString());
            invoke = getter.invoke(entity);
            map.put(fieldname, invoke);
        }
        return map;
    }

    private Pattern p1 = Pattern.compile("^.*\\..*");
    private Pattern p2 = Pattern.compile("^.*\\[(.*)\\].*");

    private Object setAttributeValue(Class<?> entityClass, Object entity, String key, Object value) throws Exception {
        Map<String, Class<?>> mapper = mea.getEntityKeyMapper(entityClass);

        Matcher m1 = p1.matcher(key);
        Matcher m2 = p2.matcher(key);

        if (m2.matches()) {
            return ArraysSetValue(entityClass, entity, key, value, mapper);
        }
        if (m1.matches()) {
            return otherRefTypeKeyValue(entityClass, entity, key, value, mapper);
        }
        if (!(m1.matches() || m2.matches())) {
            return basicKeyValue(entityClass, entity, key, value, mapper);
        }
        return entity;
    }

    private Object ArraysSetValue(Class<?> entityClass, Object entity, String key, Object value,
                                  Map<String, Class<?>> mapper) throws NoSuchFieldException, IllegalAccessException, Exception {
        Pattern p2 = Pattern.compile(".*\\[(.*)\\].*");
        Matcher m2 = p2.matcher(key);
        if (m2.matches()) {
            Integer index_ = null;
            String index = m2.group(1);

            String[] split = key.split("\\[");
            for (String subkey : split) {
                Set<Entry<String, Class<?>>> entrySet = mapper.entrySet();
                for (Entry<String, Class<?>> entry : entrySet) {
                    if (entry.getKey().equals(subkey + "[")) {
                        Class<?> subType = entry.getValue();// 数组或集合中值对应的类型
                        Field f = this.getField(entityClass, subkey);


                        f.setAccessible(true);
                        Class<?> type = f.getType();
                        if (type.isArray()) {
                            index_ = Integer.valueOf(index);
                            if (key.indexOf("]") + 1 == key.length()) {
                                Object o2 = basicGetObjec(p, subType, value);
                                return arrayToAtrriblute(entity, index_, subType, f, o2);
                            }
                            if (".".equals(key.substring(key.indexOf("]") + 1, key.indexOf("]") + 2))) {// 自定义类型数组
                                // 自定义类型 数组
                                return arrayToAtrriblute2(entity, key, value, index_, subType, f);
                            }
                            if ("[".equals(key.substring(key.indexOf("]") + 1, key.indexOf("]") + 2))) {
                                // TODO 多维数组 不处理
                                logger.debug("多维数组 该版本不处理");
                            }
                        }
                        if (List.class.isAssignableFrom(type)) {//
                            index_ = Integer.valueOf(index);
                            return listAttribute(entity, index_, subType, f, value, key);
                        }
                        if (Map.class.isAssignableFrom(type)) {
                            return mapAttribute(entity, index, subType, f, value, key);
                        }
                    }
                }

                return entity;
            }
        }

        return entity;
    }

    private Object mapAttribute(Object entity, String index, Class<?> subType, Field f, Object value, String key)
            throws Exception {

        Object subEntity = f.get(entity);
        Class<?> type = f.getType();

        if (key.indexOf("]") + 1 == key.length()) {// Map<基本内型>
            Object o2 = this.basicGetObjec(p, subType, value);

            Map map = null;

            if (subEntity == null) {
                if (Map.class == type)
                    map = new HashMap<String, Object>();
                else
                    map = (Map) type.newInstance();
            } else {
                map = (Map) subEntity;
            }
            map.put(index, o2);
            f.set(entity, map);

            return entity;

        } else {
            if (".".equals(key.substring(key.indexOf("]") + 1, key.indexOf("]") + 2))) {// Map<自定义类型>
                Map<String, Object> map = new HashMap<String, Object>();
                map.put(key.substring(key.indexOf("]") + 2), value != null ? value.toString() : "");
                Object oo = null;
                if (subEntity != null && ((Map) subEntity).get(index) != null)
                    oo = ((Map) subEntity).get(index);
                Object o2 = getBean(subType, map, oo);

                Map mapp = null;

                if (subEntity == null) {
                    if (Map.class == type)
                        mapp = new HashMap<String, Object>();
                    else
                        mapp = (Map) type.newInstance();
                } else {
                    mapp = (Map) subEntity;
                }
                mapp.put(index, o2);
                f.set(entity, mapp);
                return entity;
            } else if ("[".equals(key.substring(key.indexOf("]") + 1, key.indexOf("]") + 2))) {
                // TODO Map<数组的情形暂不处理>
                logger.debug("Map<数组>的情形该版本没做处理......");
            }

        }
        return entity;
    }

    private Object arrayToAtrriblute2(Object entity, String key, Object value, Integer index_, Class<?> subType,
                                      Field f) throws IllegalAccessException, Exception {
        Object subEntity = f.get(entity);
        Class<?> type = f.getType();
        Object[] obj = new Object[index_ + 1];
        Map<String, Object> map = new HashMap<String, Object>();
        map.put(key.substring(key.indexOf("]") + 2), value);
        Object oo = null;
        if (subEntity != null) {

            if (Array.getLength(subEntity) > index_)
                oo = Array.get(subEntity, index_);
        }
        Object o2 = getBean(subType, map, oo);

        List arrayList = null;
        if (subEntity == null) {
            arrayList = new ArrayList();
        } else {
            Object[] ob = new Object[Array.getLength(subEntity)];
            for (int i = 0; i < ob.length; i++) {
                ob[i] = Array.get(subEntity, i);
            }
            arrayList = Arrays.asList(ob);
        }
        List<Object> asList = null;

        obj[index_] = o2;
        asList = Arrays.asList(obj);

        ArrayList arrayList2 = new ArrayList(asList);
        if (arrayList2.size() >= arrayList.size()) {
            for (int i = 0; i < arrayList.size(); i++) {
                if (arrayList.get(i) != null)
                    arrayList2.set(i, arrayList.get(i));
            }
            arrayList = arrayList2;
        } else {
            for (int i = 0; i < arrayList2.size(); i++) {
                if (arrayList2.get(i) != null)
                    arrayList.set(i, arrayList2.get(i));
            }
        }
        Object newO = Array.newInstance(subType, arrayList.size());
        for (int i = 0; i < arrayList.size(); i++) {
            Array.set(newO, i, arrayList.get(i));
        }
        f.set(entity, newO);
        return entity;
    }

    private Object listAttribute(Object entity, Integer index_, Class<?> subType, Field f, Object value, String key)
            throws Exception {
        Object[] obj = new Object[index_ + 1];
        Object subEntity = f.get(entity);
        if (key.indexOf("]") + 1 == key.length()) {// List<基本内型>
            Object o2 = this.basicGetObjec(p, subType, value);

            List arrayList = null;

            if (subEntity == null) {
                arrayList = new ArrayList();
            } else {
                arrayList = (List) subEntity;
            }
            List<Object> asList = null;

            obj[index_] = o2;
            asList = Arrays.asList(obj);

            ArrayList arrayList2 = new ArrayList(asList);
            if (arrayList2.size() >= arrayList.size()) {
                for (int i = 0; i < arrayList.size(); i++) {
                    if (arrayList.get(i) != null)
                        arrayList2.set(i, arrayList.get(i));
                }
                f.set(entity, arrayList2);
            } else {
                for (int i = 0; i < arrayList2.size(); i++) {
                    if (arrayList2.get(i) != null)
                        arrayList.set(i, arrayList2.get(i));
                }
                f.set(entity, arrayList);
            }

            return entity;

        } else {
            if (".".equals(key.substring(key.indexOf("]") + 1, key.indexOf("]") + 2))) {// List<自定义类型>
                Map<String, Object> map = new HashMap<String, Object>();
                map.put(key.substring(key.indexOf("]") + 2), value != null ? value.toString() : "");
                Object oo = null;
                if (subEntity != null && ((List) subEntity).size() > index_)
                    oo = ((List) subEntity).get(index_);
                Object o2 = getBean(subType, map, oo);

                List arrayList = null;
                if (subEntity == null) {
                    arrayList = new ArrayList();
                } else {
                    arrayList = (List) subEntity;
                }
                List<Object> asList = null;

                obj[index_] = o2;
                asList = Arrays.asList(obj);

                ArrayList arrayList2 = new ArrayList(asList);
                if (arrayList2.size() >= arrayList.size()) {
                    for (int i = 0; i < arrayList.size(); i++) {
                        arrayList2.set(i, arrayList.get(i));
                    }
                    f.set(entity, arrayList2);
                } else {
                    for (int i = 0; i < arrayList2.size(); i++) {
                        arrayList.set(i, arrayList2.get(i));
                    }
                    f.set(entity, arrayList);
                }
                return entity;
            } else if ("[".equals(key.substring(key.indexOf("]") + 1, key.indexOf("]") + 2))) {
                // TODO List<数组的情形暂不处理>
                logger.debug("List<数组>的情形该版本暂没做处理......");
            }

        }
        return entity;
    }

    private Object arrayToAtrriblute(Object entity, Integer index_, Class<?> subType, Field f, Object value)
            throws IllegalAccessException {

        Object subEntity = f.get(entity);

        toArrayxxx(entity, subType, index_, f, value, subEntity);
        return entity;
    }

    private void toArrayxxx(Object entity, Class<?> subType, Integer index_, Field f, Object value, Object subEntity)
            throws IllegalAccessException {
        Object obj3 = Array.newInstance(subType, index_ + 1);
        Object obj2 = null;
        if (subEntity == null) {
            obj2 = Array.newInstance(subType, index_ + 1);
        } else {
            obj2 = subEntity;
        }
        Array.set(obj3, index_, value);

        if (Array.getLength(obj2) >= Array.getLength(obj3)) {
            for (int i = 0; i < Array.getLength(obj3); i++) {
                Array.set(obj2, i, Array.get(obj3, i));
            }
        } else {
            for (int i = 0; i < Array.getLength(obj2); i++) {
                Array.set(obj3, i, Array.get(obj2, i));
            }
            obj2 = obj3;
        }
        f.set(entity, obj2);
    }

    /**
     * @return java.lang.Object
     * @Author xh
     * @Description 除list set map array 之外的引用类型赋值处理
     * @Date 11:25 2019/6/25
     * @Param [entityClass, entity, key, value, mapper]
     **/
    private Object otherRefTypeKeyValue(Class<?> entityClass, Object entity, String key, Object value,
                                        Map<String, Class<?>> mapper) throws Exception {
        String[] split = key.split("\\.");
        for (String subkey : split) {
            Class<?> aClass = mapper.get(subkey + ".");
            if (aClass != null) {

                Field f = this.getField(entityClass, subkey);

                f.setAccessible(true);
                Object subEntity = f.get(entity);

                Map<String, Object> map = new HashMap<String, Object>();
                map.put(key.substring(subkey.length() + 1), value);
                Object o2 = this.getBean(aClass, map, subEntity);


                if (aClass.isAssignableFrom(o2.getClass())) {
                    f.set(entity, o2);
                    return entity;
                }
            }

        }
        return entity;
    }

    /**
     * @return java.lang.Object
     * @Author xh
     * @Description 基本类型的赋值处理
     * @Date 11:24 2019/6/25
     * @Param [entityClass, entity, key, value, mapper]
     **/
    private Object basicKeyValue(Class<?> entityClass, Object entity, String key, Object value,
                                 Map<String, Class<?>> mapper) throws Exception {
        Set<Entry<String, Class<?>>> entrySet = mapper.entrySet();
        Class<?> aClass = mapper.get(key);
        if (aClass != null) {
            Field f = this.getField(entityClass, key);

            f.setAccessible(true);

            if (String.class.isAssignableFrom(aClass)) {//如是字符串
                f.set(entity, value.toString());
            } else if (Date.class.isAssignableFrom(aClass) && value instanceof String) {//如果字段上有@DateFormat注解，就按注解的value字段值所对应的格式去解析 value,并赋此字段
                DateFormat annotation = f.getAnnotation(DateFormat.class);
                if (annotation != null) {
                    String dateFormat = annotation.value();
                    SimpleDateFormat df = new SimpleDateFormat(dateFormat);
                    Date parse = df.parse(value.toString());
                    f.set(entity, parse);
                } else {
                    Object value2 = basicGetObjec(p, aClass, value);
                    f.set(entity, value2);
                }
            } else {//其它基本类型的处理
                Object value2 = basicGetObjec(p, aClass, value);
                f.set(entity, value2);
            }
        }

        return entity;
    }

    private Object basicGetObjec(MyPropertyEditorRegistrySupport p2, Class<?> clz, Object value) {
        if (clz.isAssignableFrom(String.class)) {
            if (value instanceof Date) {//如果value是日期 默认格式化为 yyyy-MM-dd HH:mm:ss 格式
                PropertyEditor de = p.getDefaultEditor(Date.class);
                de.setValue(value);
                value = de.getAsText();
            }
            return value != null ? value.toString() : null;
        } else {
            if (value == null) return null;
            if (clz.isAssignableFrom(value.getClass())) return value;
            PropertyEditor de = p.getDefaultEditor(clz);
            if (de == null) {
                //枚举类型的处理
                try {
                    if (clz.isEnum()) {
                        value = Enum.valueOf((Class) clz, value.toString());
                        return value;
                    }
                } catch (Exception e) {
                    logger.debug(e.getMessage());
                }
                return null;
            }
            PropertyEditor de2 = p.getDefaultEditor(value.getClass());
            if (de2 != null) de2.setValue(value);

            de.setAsText(value != null ? (de2 == null ? value.toString() : de2.getAsText()) : "");
            return de.getValue();
        }
    }

    /**
     * @return java.lang.Object
     * @Author xh
     * @Description 值类型转换
     * @Date 13:13 2019/11/21
     * @Param [clz, value]
     **/
    public Object convertValueType(Class<?> clz, Object value) {
        return this.basicGetObjec(p, clz, value);
    }
}
